using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using RebelStar.Engine.Maps;
using RebelStar.Engine.Players.Fog;
using RebelStar.Engine.Units;
using RebelStar.Engine.Units.Equipments.Weapons;

namespace RebelStar.Engine.Players
{
    public class Player
    {
        private readonly Dictionary<MapEntry, MapFogEntry> fog;
        private readonly List<Unit> units;
        private readonly Map map;
        private readonly string name;
        private Unit selectedUnit;

        internal Player(Map map, string name)
        {
            this.map = map;
            this.name = name;

            this.units = new List<Unit>();
            this.fog = new Dictionary<MapEntry, MapFogEntry>(map.Height*map.Width);
            foreach (MapEntry entry in map)
                fog.Add(entry, new MapFogEntry(entry, FogStatus.Blackout));

            Unit unit = new Unit(map.UnitSprites[1], new Point(2, 5));
            this.Units.Add(unit);
            unit.Slots[1].Weapon = new Weapon("Pistol", 3, 0.6f, 3, 5);

            unit = new Unit(map.UnitSprites[0], new Point(map.Width-1, map.Height-1));
            this.Units.Add(unit);
            unit.Slots[0].Weapon = new Weapon("Pistol", 3, 0.6f, 3, 5);
            unit.Slots[1].Weapon = new Weapon("Rifle", 7, 0.9f, 5, 7);
        }

        public void RefreshFog()
        {
            foreach (MapEntry entry in fog.Keys)
                fog[entry].ResetStatus();

            foreach (Unit unit in units)
            {
                for (int i = unit.Position.Y - unit.Sight*2; i < unit.Position.Y + unit.Sight*2 + 1; i++)
                    for (int j = unit.Position.X - unit.Sight; j < unit.Position.X + unit.Sight + 1; j++)
                        if (j >= 0 && i >= 0 && j < map.Width && i < map.Height)
                            fog[map[j, i]].MergeStatus(FogStatus.Visible);
                for (int j = unit.Position.X - unit.Sight-1; j < unit.Position.X + unit.Sight + 2; j++)
                    if (j >= 0 && j < map.Width)
                    {
                        int i = unit.Position.Y - unit.Sight*2 - 1;
                        if (i >= 0 && i < map.Height)
                            fog[map[j, i]].MergeStatus(FogStatus.Semivisible);
                        i--;
                        if (i >= 0 && i < map.Height)
                            fog[map[j, i]].MergeStatus(FogStatus.Semivisible);
                        i = unit.Position.Y + unit.Sight*2 + 1;
                        if (i >= 0 && i < map.Height)
                            fog[map[j, i]].MergeStatus(FogStatus.Semivisible);
                        i++;
                        if (i >= 0 && i < map.Height)
                            fog[map[j, i]].MergeStatus(FogStatus.Semivisible);
                    }
                for (int i = unit.Position.Y - unit.Sight*2-1; i < unit.Position.Y + unit.Sight*2 + 2; i++)
                    if (i >= 0 && i < map.Height)
                    {
                        int j = unit.Position.X - unit.Sight - 1;
                        if (j >= 0 && j < map.Width)
                            fog[map[j, i]].MergeStatus(FogStatus.Semivisible);
                        j = unit.Position.X + unit.Sight + 1;
                        if (j >= 0 && j < map.Width)
                            fog[map[unit.Position.X + unit.Sight + 1, i]].MergeStatus(FogStatus.Semivisible);
                    }
            }
        }
        public void KeyPress(Keys keyCode)
        {
            if (selectedUnit == null) return;

            MoveType move = MoveType.None;
            switch (keyCode)
            {
                case Keys.NumPad1:
                    move = MoveType.LeftDown;
                    break;
                case Keys.Down:
                case Keys.NumPad2:
                    move = MoveType.Down;
                    break;
                case Keys.NumPad3:
                    move = MoveType.RightDown;
                    break;
                case Keys.Left:
                case Keys.NumPad4:
                    move = MoveType.Left;
                    break;
                case Keys.Right:
                case Keys.NumPad6:
                    move = MoveType.Right;
                    break;
                case Keys.NumPad7:
                    move = MoveType.LeftUp;
                    break;
                case Keys.Up:
                case Keys.NumPad8:
                    move = MoveType.Up;
                    break;
                case Keys.NumPad9:
                    move = MoveType.RightUp;
                    break;
                default:
                    break;
            }
            if (move != MoveType.None)
            {
                Point destPoint = selectedUnit.CalculateMove(move);
                if (map.Contains(destPoint))
                {
                    MapEntry dest = map[destPoint];
                    if (dest.IsPassable &&
                        selectedUnit.CurrentAP >= dest.AvgMP)
                    {
                        selectedUnit.Move(move, dest.AvgMP);
                        RefreshFog();
                        map.Invalidate();
                    }
                }
            }
        }
        public void ResetParameters()
        {
            foreach (Unit unit in units)
                unit.ResetParameters();
        }
        /// <summary>
        /// Find player unit at specified position
        /// </summary>
        /// <param name="position">Map Position</param>
        /// <returns>Returns Unit found or null else</returns>
        public Unit GetUnit(Point position)
        {
            if (position == Point.Zero) return null;

            foreach (Unit unit in units)
                if (unit.Position == position)
                    return unit;

            return null;
        }
        internal void SelectUnit(Unit unit)
        {
            selectedUnit = unit == null || !unit.IsAlive ? null : unit;
        }

        public List<Unit> Units
        {
            get { return units; }
        }
        internal Dictionary<MapEntry, MapFogEntry> Fog
        {
            get { return fog; }
        }
        public Unit SelectedUnit
        {
            get { return selectedUnit; }
        }
        public string Name
        {
            get { return name; }
        }
        public bool IsAlive
        {
            get
            {
                return units.TrueForAll(delegate(Unit unit) { return unit.IsAlive; });
            }
        }

        public override string ToString()
        {
            return name;
        }
    }
}